[Go Package] 主流 Go 日志方案 logrus + rotatelogs + lfshook 您所在的位置:网站首页 zero hour设置 [Go Package] 主流 Go 日志方案 logrus + rotatelogs + lfshook

[Go Package] 主流 Go 日志方案 logrus + rotatelogs + lfshook

2024-01-26 10:19| 来源: 网络整理| 查看: 265

一、综述

Gin自带的日志功能较弱,

使用logrus(sirupsen/logrus: Structured, pluggable logging for Go. )定制日志内容,核心函数: func (logger *Logger) WithFields(fields Fields) *Entry //Fields 的实际类型是 map[string]interface{} ,包含了自定义日志的各个字段名和字段值 使用rotatelogs完成日志分割、日志定期清理、生成软链文件指向最新日志。核心函数: func New(p string, options ...Option) (*RotateLogs, error) 使用lfshook决定哪些级别的日志可以使用rotatelogs的切割设置,并决定输出格式(TEXT / JSON)。 核心数据结构是一个 WriteMap,记录可用于输出的日志级别;核心函数是NewHook(),加载WriteMap和输出格式(TEXT /JSON),并返回一个logrus可挂载的hook。 type WriterMap map[logrus.Level]io.Writer func NewHook(output interface{}, formatter logrus.Formatter) *LfsHook 二、rotatelogs和lfshook的配合

log 是logrus的一个实例

// 设置日志切割 rotatelogs writer, _ := rotatelogs.New( filePath+"%Y%m%d.log", //在项目根目录下生成软链文件 latest_log.log 指向最新的日志文件。注意!!!必须在管理员权限下开终端启动。 rotatelogs.WithLinkName(linkName), //日志最大保存时间 rotatelogs.WithMaxAge(7*24*time.Hour), ////设置日志切割时间间隔(1天)(隔多久分割一次) rotatelogs.WithRotationTime(24*time.Hour), ) // lfshook 决定哪些日志级别可用日志分割 writeMap := lfshook.WriterMap{ logrus.PanicLevel: writer, logrus.FatalLevel: writer, logrus.ErrorLevel: writer, logrus.WarnLevel: writer, logrus.InfoLevel: writer, logrus.DebugLevel: writer, } // 配置 lfshook hook := lfshook.NewHook(writeMap, &logrus.TextFormatter{ // 设置日期格式 TimestampFormat: "2006.01.02 - 15:04:05", }) //为 logrus 实例添加自定义 hook log.AddHook(hook) 三、logrus的使用 1.基本用法

logrus 可以取代自带的log,局部地使用

package main import ( log "github.com/sirupsen/logrus" ) func main() { log.WithFields(log.Fields{ "animal": "walrus", }).Info("A walrus appears") }

上面代码执行后,标准输出上输出如下:

time="2022-08-22T15:42:22+08:00" level=info msg="A walrus appears" animal=walrus

 logrus可以通过简单的配置,来定义输出格式或者日志级别。

package main import ( "os" log "github.com/sirupsen/logrus" ) func init() { // 设置日志格式为json格式 log.SetFormatter(&log.JSONFormatter{}) // 设置将日志输出到标准输出(默认的输出为stderr,标准错误) // 日志消息输出可以是任意的io.writer类型 log.SetOutput(os.Stdout) // 设置日志级别为warn以上 log.SetLevel(log.WarnLevel) } func main() { log.WithFields(log.Fields{ "animal": "walrus", "size": 10, }).Info("A group of walrus emerges from the ocean") log.WithFields(log.Fields{ "omg": true, "number": 122, }).Warn("The group's number increased tremendously!") log.WithFields(log.Fields{ "omg": true, "number": 100, }).Fatal("The ice breaks!") } 2.自定义Logger

如果想在一个应用里面向多个地方log,可以创建Logger实例。 logger是一种相对高级的用法,,对于一个大型项目, 往往需要一个全局的logrus实例,即logger对象来记录项目所有的日志。如:

package main import ( "github.com/sirupsen/logrus" "os" ) // logrus提供了New()函数来创建一个logrus的实例. // 项目中,可以创建任意数量的logrus实例. var log = logrus.New() func main() { // 为当前logrus实例设置消息的输出,同样地, // 可以设置logrus实例的输出到任意io.writer log.Out = os.Stdout // 为当前logrus实例设置消息输出格式为json格式. // 同样地,也可以单独为某个logrus实例设置日志级别和hook,这里不详细叙述. log.Formatter = &logrus.JSONFormatter{} log.WithFields(logrus.Fields{ "animal": "walrus", "size": 10, }).Info("A group of walrus emerges from the ocean") } 3.Fields用法

logrus推荐使用Fields来进行精细化的,结构化的信息记录,如下:

log.WithFields(log.Fields{ "event": event, "topic": topic, "key": key, }).Fatal("Failed to send event")

WithFields()可以规范使用者按照其提倡的方式记录日志。但是WithFields()依然是可选的,因为某些场景下只需记录一条简单的消息。

通常在一个应用中,都有一些固定的Field。比如在处理用户http请求时,上下文中,所有的日志都会有request_id和user_ip。为了避免每次记录日志都要使用

log.WithFields(log.Fields{“request_id”: request_id, “user_ip”: user_ip})

我们可以创建一个logrus.Entry实例,为这个实例设置默认Fields,在上下文中使用这个logrus.Entry实例记录日志即可。

entry := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}) //↓ will log request_id and user_ip entryr.Info("something happened on that request") entry.Warn("something not great happened") 4.Hook接口用法

logrus最令人心动的功能就是其可扩展的HOOK机制了,通过在初始化时为logrus添加hook,logrus可以实现各种扩展功能.

logrus的hook接口定义如下,其原理是每此写入日志时拦截,修改logrus.Entry.

// logrus在记录Levels()返回的日志级别的消息时会触发HOOK, // 按照Fire方法定义的内容修改logrus.Entry. type Hook interface { Levels() []Level Fire(*Entry) error }

一个简单自定义hook如下,DefaultFieldHook定义会在所有级别的日志消息中加入默认字段appName=”myAppName”.

type DefaultFieldHook struct { } func (hook *DefaultFieldHook) Fire(entry *log.Entry) error { entry.Data["appName"] = "MyAppName" return nil } func (hook *DefaultFieldHook) Levels() []log.Level { return log.AllLevels }

hook的使用也很简单,在初始化前调用log.AddHook(hook)添加相应的hook即可.

logrus官方仅仅内置了syslog的hook. 此外,但Github也有很多第三方的hook可供使用,文末将提供一些第三方HOOK的连接.

4.1 Logrus-Hook-Email

email这里只需用NewMailAuthHook方法得到hook,再添加即可

func Email(){ logger:= logrus.New() //parameter"APPLICATION_NAME", "HOST", PORT, "FROM", "TO" //首先开启smtp服务,最后两个参数是smtp的用户名和密码 hook, err := logrus_mail.NewMailAuthHook("testapp", "smtp.163.com",25,"[email protected]","[email protected]","smtp_name","smtp_password") if err == nil { logger.Hooks.Add(hook) } //生成*Entry var filename="123.txt" contextLogger :=logger.WithFields(logrus.Fields{ "file":filename, "content": "GG", }) //设置时间戳和message contextLogger.Time=time.Now() contextLogger.Message="这是一个hook发来的邮件" //只能发送Error,Fatal,Panic级别的log contextLogger.Level=logrus.FatalLevel //使用Fire发送,包含时间戳,message hook.Fire(contextLogger) } 4.2 Logrus-Hook-Slack

安装slackrus github.com/johntdyer/slackrus

package main import ( logrus "github.com/sirupsen/logrus" "github.com/johntdyer/slackrus" "os" ) func main() { logrus.SetFormatter(&logrus.JSONFormatter{}) logrus.SetOutput(os.Stderr) logrus.SetLevel(logrus.DebugLevel) logrus.AddHook(&slackrus.SlackrusHook{ HookURL: "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz", AcceptedLevels: slackrus.LevelThreshold(logrus.DebugLevel), Channel: "#slack-testing", IconEmoji: ":ghost:", Username: "foobot", }) logrus.Warn("warn") logrus.Info("info") logrus.Debug("debug") } HookURL: 填写slack web-hook地址 AcceptedLevels: 设置日志输出级别 Channel: 设置日志频道 Username: 设置需要@的用户名 四、Go Blog 项目中的一个完整的log中间件 package middleware import ( "fmt" "github.com/gin-gonic/gin" rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/rifflock/lfshook" "github.com/sirupsen/logrus" "os" "time" ) func Logger() gin.HandlerFunc { log := logrus.New() // 设置输出文件 filePath := "log/" linkName := "latest_log.log" // 打开指定处的文件,并指定权限为:可读可写,可创建 src, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0755) //0755-> rwx r-x r-x linux知识 if err != nil { fmt.Println("err:", err) } log.Out = src // 设置日志级别。低于 Debug 级别的 Trace 将不会被打印 log.SetLevel(logrus.DebugLevel) // 设置日志切割 rotatelogs writer, _ := rotatelogs.New( filePath+"%Y%m%d.log", //在项目根目录下生成软链文件 latest_log.log 指向最新的日志文件。注意!!!必须在管理员权限下开终端启动。 rotatelogs.WithLinkName(linkName), //日志最大保存时间 rotatelogs.WithMaxAge(7*24*time.Hour), ////设置日志切割时间间隔(1天)(隔多久分割一次) rotatelogs.WithRotationTime(24*time.Hour), ) // lfshook 决定哪些日志级别可用日志分割 writeMap := lfshook.WriterMap{ logrus.PanicLevel: writer, logrus.FatalLevel: writer, logrus.ErrorLevel: writer, logrus.WarnLevel: writer, logrus.InfoLevel: writer, logrus.DebugLevel: writer, } // 配置 lfshook hook := lfshook.NewHook(writeMap, &logrus.TextFormatter{ // 设置日期格式 TimestampFormat: "2006.01.02 - 15:04:05", }) //为 logrus 实例添加自定义 hook log.AddHook(hook) return func(c *gin.Context) { // 一.配置所需的 Fields startTime := time.Now() c.Next() spendTime := time.Since(startTime).Milliseconds() ST := fmt.Sprintf("%d ms", spendTime) // 1.API 调用耗时 hostName, err := os.Hostname() // 2.主机名 if err != nil { hostName = "unknown" } statusCode := c.Writer.Status() // 3.状态码 clientIP := c.ClientIP() // 4.请求客户端的 IP userAgent := c.Request.UserAgent() // 5.用户代理,通常是某个浏览器。dev环境下是apipost dataSize := c.Writer.Size() // 6.响应报文 body 的字节长度 if dataSize < 0 { dataSize = 0 } method := c.Request.Method // 7.请求方法 path := c.Request.RequestURI // 8.请求 URL // 二.从标准记录器创建一个条目,并向其中添加多个字段(隐式添加 log 本身的时间戳,信息等 fields ) entry := log.WithFields(logrus.Fields{ "HostName": hostName, "Status": statusCode, "SpendTime": ST, "IP": clientIP, "UserAgent": userAgent, "Method": method, "DataSize": dataSize, "Path": path, }) // Errors 保存了使用当前context的所有中间件/handler 所产生的全部错误信息。 // 源码注释: Errors is a list of errors attached to all the handlers/middlewares who used this context. // 三.将系统内部的错误 log 出去 if len(c.Errors) > 0 { log.Error(c.Errors.ByType(gin.ErrorTypePrivate).String()) } // 四.根据状态码决定打印 log 的等级 if statusCode >= 500 { entry.Error() } else if statusCode >= 400 { entry.Warn() } else { entry.Info() } } } PS: 一些有用的blog Go进阶10:logrus日志使用教程 (mojotv.cn)


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有